/*
 * This file is part of the openHiTLS project.
 *
 * openHiTLS is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <stddef.h>
#include "hitls_build.h"
#include "config_type.h"
#include "hitls_crypt_type.h"
#include "tls_config.h"
#include "hitls_error.h"
#include "crypt_algid.h"
#ifdef HITLS_TLS_FEATURE_PROVIDER
#include "securec.h"
#include "crypt_eal_provider.h"
#include "crypt_params_key.h"
#include "crypt_eal_implprovider.h"
#include "crypt_eal_pkey.h"
#endif

static int32_t UpdateGroupsArray(TLS_Config *config, const TLS_GroupInfo *groups, uint32_t groupInfolen)
{
    if (config == NULL || groups == NULL || groupInfolen == 0) {
        return HITLS_INVALID_INPUT;
    }
    uint32_t size = 0;
    uint16_t *tempGroups = BSL_SAL_Calloc(groupInfolen, sizeof(uint16_t));
    if (tempGroups == NULL) {
        return HITLS_MEMALLOC_FAIL;
    }
    for (uint32_t i = 0; i < groupInfolen; i++) {
        if ((config->version & groups[i].versionBits) != 0) {
            bool isDuplicate = false;
            // Check if this groupId already exists
            for (uint32_t j = 0; j < size; j++) {
                if (tempGroups[j] == groups[i].groupId) {
                    isDuplicate = true;
                    break;
                }
            }
            if (!isDuplicate) {
                tempGroups[size] = groups[i].groupId;
                size++;
            }
        }
    }

    if (size == 0) {
        BSL_SAL_Free(tempGroups);
        return HITLS_INVALID_INPUT;
    }

    BSL_SAL_FREE(config->groups);
    config->groups = tempGroups;
    config->groupsSize = size;
    return HITLS_SUCCESS;
}

#ifndef HITLS_TLS_FEATURE_PROVIDER
static const TLS_GroupInfo GROUP_INFO[] = {
    {
        "secp256r1",
        CRYPT_ECC_NISTP256, // CRYPT_ECC_NISTP256
        CRYPT_PKEY_ECDH, // CRYPT_PKEY_ECDH
        128, // secBits
        HITLS_EC_GROUP_SECP256R1, // groupId
        65, 32, 0, // pubkeyLen=65, sharedkeyLen=32 (256 bits)
        TLS_VERSION_MASK | DTLS_VERSION_MASK, // versionBits
        false,
    },
    {
        "secp384r1",
        CRYPT_ECC_NISTP384, // CRYPT_ECC_NISTP384
        CRYPT_PKEY_ECDH, // CRYPT_PKEY_ECDH
        192, // secBits
        HITLS_EC_GROUP_SECP384R1, // groupId
        97, 48, 0, // pubkeyLen=97, sharedkeyLen=48 (384 bits)
        TLS_VERSION_MASK | DTLS_VERSION_MASK, // versionBits
        false,
    },
    {
        "secp521r1",
        CRYPT_ECC_NISTP521, // CRYPT_ECC_NISTP521
        CRYPT_PKEY_ECDH, // CRYPT_PKEY_ECDH
        256, // secBits
        HITLS_EC_GROUP_SECP521R1, // groupId
        133, 66, 0, // pubkeyLen=133, sharedkeyLen=66 (521 bits)
        TLS_VERSION_MASK | DTLS_VERSION_MASK, // versionBits
        false,
    },
    {
        "brainpoolP256r1",
        CRYPT_ECC_BRAINPOOLP256R1, // CRYPT_ECC_BRAINPOOLP256R1
        CRYPT_PKEY_ECDH, // CRYPT_PKEY_ECDH
        128, // secBits
        HITLS_EC_GROUP_BRAINPOOLP256R1, // groupId
        65, 32, 0, // pubkeyLen=65, sharedkeyLen=32 (256 bits)
        TLS10_VERSION_BIT| TLS11_VERSION_BIT|TLS12_VERSION_BIT | DTLS_VERSION_MASK, // versionBits
        false,
    },
    {
        "brainpoolP384r1",
        CRYPT_ECC_BRAINPOOLP384R1, // CRYPT_ECC_BRAINPOOLP384R1
        CRYPT_PKEY_ECDH, // CRYPT_PKEY_ECDH
        192, // secBits
        HITLS_EC_GROUP_BRAINPOOLP384R1, // groupId
        97, 48, 0, // pubkeyLen=97, sharedkeyLen=48 (384 bits)
        TLS10_VERSION_BIT| TLS11_VERSION_BIT|TLS12_VERSION_BIT | DTLS_VERSION_MASK, // versionBits
        false,
    },
    {
        "brainpoolP512r1",
        CRYPT_ECC_BRAINPOOLP512R1, // CRYPT_ECC_BRAINPOOLP512R1
        CRYPT_PKEY_ECDH, // CRYPT_PKEY_ECDH
        256, // secBits
        HITLS_EC_GROUP_BRAINPOOLP512R1, // groupId
        129, 64, 0, // pubkeyLen=129, sharedkeyLen=64 (512 bits)
        TLS10_VERSION_BIT| TLS11_VERSION_BIT|TLS12_VERSION_BIT | DTLS_VERSION_MASK, // versionBits
        false,
    },
    {
        "x25519",
        CRYPT_PKEY_PARAID_MAX, // CRYPT_ECC_X25519
        CRYPT_PKEY_X25519, // CRYPT_PKEY_ECDH
        128, // secBits
        HITLS_EC_GROUP_CURVE25519, // groupId
        32, 32, 0, // pubkeyLen=32, sharedkeyLen=32 (256 bits)
        TLS_VERSION_MASK | DTLS_VERSION_MASK, // versionBits
        false,
    },
    {
        "sm2",
        CRYPT_PKEY_PARAID_MAX, // CRYPT_PKEY_PARAID_MAX
        CRYPT_PKEY_SM2, // CRYPT_PKEY_SM2
        128, // secBits
        HITLS_EC_GROUP_SM2, // groupId
        65, 32, 0, // pubkeyLen=65, sharedkeyLen=32 (256 bits)
        TLCP11_VERSION_BIT | DTLCP11_VERSION_BIT, // versionBits
        false,
    },
    {
        "ffdhe2048",
        CRYPT_DH_RFC3526_2048, // CRYPT_DH_2048
        CRYPT_PKEY_DH, // CRYPT_PKEY_DH
        112, // secBits
        HITLS_FF_DHE_2048, // groupId
        256, 256, 0, // pubkeyLen=256, sharedkeyLen=256 (2048 bits)
        TLS13_VERSION_BIT, // versionBits
        false,
    },
    {
        "ffdhe3072",
        CRYPT_DH_RFC3526_3072, // Fixed constant name
        CRYPT_PKEY_DH,
        128,
        HITLS_FF_DHE_3072,
        384, 384, 0, // pubkeyLen=384, sharedkeyLen=384 (3072 bits)
        TLS13_VERSION_BIT,
        false,
    },
    {
        "ffdhe4096",
        CRYPT_DH_RFC7919_4096, // CRYPT_DH_4096
        CRYPT_PKEY_DH, // CRYPT_PKEY_DH
        128, // secBits
        HITLS_FF_DHE_4096, // groupId
        512, 512, 0, // pubkeyLen=512, sharedkeyLen=512 (4096 bits)
        TLS13_VERSION_BIT, // versionBits
        false,
    },
    {
        "ffdhe6144",
        CRYPT_DH_RFC7919_6144, // CRYPT_DH_6144
        CRYPT_PKEY_DH, // CRYPT_PKEY_DH
        128, // secBits
        HITLS_FF_DHE_6144, // groupId
        768, 768, 0, // pubkeyLen=768, sharedkeyLen=768 (6144 bits)
        TLS13_VERSION_BIT, // versionBits
        false,
    },
    {
        "ffdhe8192",
        CRYPT_DH_RFC7919_8192, // CRYPT_DH_8192
        CRYPT_PKEY_DH, // CRYPT_PKEY_DH
        192, // secBits
        HITLS_FF_DHE_8192, // groupId
        1024, 1024, 0, // pubkeyLen=1024, sharedkeyLen=1024 (8192 bits)
        TLS13_VERSION_BIT, // versionBits
        false,
    }
};

int32_t ConfigLoadGroupInfo(HITLS_Config *config)
{
    if (config == NULL) {
        return HITLS_INVALID_INPUT;
    }
    return UpdateGroupsArray(config, GROUP_INFO, sizeof(GROUP_INFO) / sizeof(TLS_GroupInfo));
}

const TLS_GroupInfo *ConfigGetGroupInfo(const HITLS_Config *config, uint16_t groupId)
{
    (void)config;
    for (uint32_t i = 0; i < sizeof(GROUP_INFO) / sizeof(TLS_GroupInfo); i++) {
        if (GROUP_INFO[i].groupId == groupId) {
            return &GROUP_INFO[i];
        }
    }
    return NULL;
}

const TLS_GroupInfo *ConfigGetGroupInfoList(const HITLS_Config *config, uint32_t *size)
{
    (void)config;
    *size = sizeof(GROUP_INFO) / sizeof(GROUP_INFO[0]);
    return &GROUP_INFO[0];
}
#else

static int32_t ProviderAddGroupInfo(const BSL_Param *params, void *args)
{
    if (params == NULL || args == NULL) {
        return HITLS_INVALID_INPUT;
    }
    TLS_CapabilityData *data = (TLS_CapabilityData *)args;
    TLS_Config *config = data->config;
    TLS_GroupInfo *group = NULL;
    CRYPT_EAL_PkeyCtx *pkey = NULL;
    BSL_Param *param = NULL;
    int32_t ret = HITLS_CONFIG_ERR_LOAD_GROUP_INFO;
    if (config->groupInfolen == config->groupInfoSize) {
        
        void *ptr = BSL_SAL_Realloc(config->groupInfo,
            (config->groupInfoSize + TLS_CAPABILITY_LIST_MALLOC_SIZE) * sizeof(TLS_GroupInfo),
            config->groupInfoSize * sizeof(TLS_GroupInfo));
        if (ptr == NULL) {
            return HITLS_MEMALLOC_FAIL;
        }
        config->groupInfo = ptr;
        (void)memset_s(config->groupInfo + config->groupInfoSize,
            TLS_CAPABILITY_LIST_MALLOC_SIZE * sizeof(TLS_GroupInfo),
            0,
            TLS_CAPABILITY_LIST_MALLOC_SIZE * sizeof(TLS_GroupInfo));
        config->groupInfoSize += TLS_CAPABILITY_LIST_MALLOC_SIZE;
    }
    group = config->groupInfo + config->groupInfolen;
    param = BSL_PARAM_FindParam((BSL_Param *)(uintptr_t)params, CRYPT_PARAM_CAP_TLS_GROUP_IANA_GROUP_NAME);
    if (param == NULL || param->valueType != BSL_PARAM_TYPE_OCTETS_PTR) {
        goto ERR;
    }
    group->name = BSL_SAL_Calloc(param->valueLen + 1, sizeof(char));
    if (group->name == NULL) {
        goto ERR;
    }
    (void)memcpy_s(group->name, param->valueLen + 1, param->value, param->valueLen);
    param = BSL_PARAM_FindParam((BSL_Param *)(uintptr_t)params, CRYPT_PARAM_CAP_TLS_GROUP_IANA_GROUP_ID);
    if (param == NULL || param->valueType != BSL_PARAM_TYPE_UINT16) {
        goto ERR;
    }
    group->groupId = *(uint16_t *)param->value;
    param = BSL_PARAM_FindParam((BSL_Param *)(uintptr_t)params, CRYPT_PARAM_CAP_TLS_GROUP_PARA_ID);
    if (param == NULL || param->valueType != BSL_PARAM_TYPE_INT32) {
        goto ERR;
    }
    group->paraId = *(int32_t *)param->value;
    param = BSL_PARAM_FindParam((BSL_Param *)(uintptr_t)params, CRYPT_PARAM_CAP_TLS_GROUP_ALG_ID);
    if (param == NULL || param->valueType != BSL_PARAM_TYPE_INT32) {
        goto ERR;
    }
    group->algId = *(int32_t *)param->value;
    param = BSL_PARAM_FindParam((BSL_Param *)(uintptr_t)params, CRYPT_PARAM_CAP_TLS_GROUP_SEC_BITS);
    if (param == NULL || param->valueType != BSL_PARAM_TYPE_INT32) {
        goto ERR;
    }
    group->secBits = *(int32_t *)param->value;
    param = BSL_PARAM_FindParam((BSL_Param *)(uintptr_t)params, CRYPT_PARAM_CAP_TLS_GROUP_VERSION_BITS);
    if (param == NULL || param->valueType != BSL_PARAM_TYPE_UINT32) {
        goto ERR;
    }
    group->versionBits = *(uint32_t *)param->value;
    param = BSL_PARAM_FindParam((BSL_Param *)(uintptr_t)params, CRYPT_PARAM_CAP_TLS_GROUP_IS_KEM);
    if (param == NULL || param->valueType != BSL_PARAM_TYPE_BOOL) {
        goto ERR;
    }
    group->isKem = *(bool *)param->value;
    param = BSL_PARAM_FindParam((BSL_Param *)(uintptr_t)params, CRYPT_PARAM_CAP_TLS_GROUP_PUBKEY_LEN);
    if (param == NULL || param->valueType != BSL_PARAM_TYPE_INT32) {
        goto ERR;
    }
    group->pubkeyLen = *(uint32_t *)param->value;
    param = BSL_PARAM_FindParam((BSL_Param *)(uintptr_t)params, CRYPT_PARAM_CAP_TLS_GROUP_SHAREDKEY_LEN);
    if (param == NULL || param->valueType != BSL_PARAM_TYPE_INT32) {
        goto ERR;
    }
    group->sharedkeyLen = *(uint32_t *)param->value;
    param = BSL_PARAM_FindParam((BSL_Param *)(uintptr_t)params, CRYPT_PARAM_CAP_TLS_GROUP_CIPHERTEXT_LEN);
    if (param == NULL || param->valueType != BSL_PARAM_TYPE_INT32) {
        goto ERR;
    }
    group->ciphertextLen = *(uint32_t *)param->value;
    ret = HITLS_SUCCESS;
    pkey = CRYPT_EAL_ProviderPkeyNewCtx(LIBCTX_FROM_CONFIG(config), group->algId, CRYPT_EAL_PKEY_EXCH_OPERATE, ATTRIBUTE_FROM_CONFIG(config));
    if (pkey != NULL) {
        config->groupInfolen++;
        CRYPT_EAL_PkeyFreeCtx(pkey);
        group = NULL;
    }
    
ERR:
    if (group != NULL) {
        BSL_SAL_Free(group->name);
        (void)memset_s(group, sizeof(TLS_GroupInfo), 0, sizeof(TLS_GroupInfo));
    }
    return ret;
}   


static int32_t ProviderLoadGroupInfo(CRYPT_EAL_ProvMgrCtx *ctx, void *args)
{
    if (ctx == NULL || args == NULL) {
        return HITLS_INVALID_INPUT;
    }
    TLS_CapabilityData data = {
        .config = (TLS_Config *)args,
        .provMgrCtx = ctx,
    };
    return CRYPT_EAL_ProviderGetCaps(ctx, CRYPT_EAL_GET_GROUP_CAP, ProviderAddGroupInfo, &data);
}

int32_t ConfigLoadGroupInfo(HITLS_Config *config)
{
    HITLS_Lib_Ctx *libCtx = LIBCTX_FROM_CONFIG(config);
    int32_t ret = CRYPT_EAL_ProviderProcAll(libCtx, ProviderLoadGroupInfo, config);
    if (ret != HITLS_SUCCESS) {
        return ret;
    }
    return UpdateGroupsArray(config, config->groupInfo, config->groupInfolen);
}

const TLS_GroupInfo *ConfigGetGroupInfo(const HITLS_Config *config, uint16_t groupId)
{
    (void)config;
    for (uint32_t i = 0; i < config->groupInfolen; i++) {
        if (config->groupInfo[i].groupId == groupId) {
            return &config->groupInfo[i];
        }
    }
    return NULL;
}

const TLS_GroupInfo *ConfigGetGroupInfoList(const HITLS_Config *config, uint32_t *size)
{
    (void)config;
    *size = config->groupInfolen;
    return config->groupInfo;
}
#endif
